import threading
import socket
import time

HBSOCKET_PING='PING'
HBSOCKET_PONG='PONG'

class HeartbeatSocket:
    def __init__(self, HBSocket=None):
        if HBSocket:
            self.sock = HBSocket
        else:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    def listen(self, HBPort):
        self.sock.bind(('', HBPort))
        self.sock.listen(1)

    def accept(self):
        csock = HeartbeatSocket(self.sock.accept()[0])
        csock.pong()
        csock.sock.close()
        

    def connect(self, HBHost, HBPort=17201):
        self.sock.connect((HBHost, HBPort))

    def ping(self):
        self.sock.send(HBSOCKET_PING)
        pong = ''
        while len(pong) < len(HBSOCKET_PONG):
            pong += self.sock.recv(len(HBSOCKET_PONG))
        if pong != HBSOCKET_PONG:
            raise RuntimeError('Remote host is down')

    def pong(self):
        ping = ''
        while len(ping) < len(HBSOCKET_PING):
            ping += self.sock.recv(len(HBSOCKET_PING))
        if ping != HBSOCKET_PING:
            return
        self.sock.send(HBSOCKET_PONG)

#TBD timed socket - sys calls block by limited time
def HeartbeatListener_threadProc(HBSock, HBEvent):
    while not HBEvent.isSet():
        HBSock.accept()
        time.sleep(5)
    HBSock.sock.close()

class HeartbeatListener:
    def __init__(self, port=17201):
        self.sock = HeartbeatSocket()
        self.sock.listen(port)

        self.heartbeatThread_stop = threading.Event()
        self.heartbeatThread_stop.clear()
        self.heartbeatThread = threading.Thread(group=None, target=HeartbeatListener_threadProc, name='heartbeat_thread', args=(self.sock, self.heartbeatThread_stop))

    def start(self):
        self.heartbeatThread.start()

    def stop(self):
        self.heartbeatThread_stop.set()
        self.heartbeatThread.join()
